/**
  ******************************************************************************
  * @file    EEPROM_Driver.c
  * @author  tuancheng_Zero
  * @version V0.1
  * @date    08/05/2024
  * @brief   EEPROM Driver
  ******************************************************************************
*/ 
#include <includes.h>
#include "EEPROM_Driver.h"

// 局部宏定义
#define EEPROM_CS_LOW()         GPIO_ResetBits(EEPROM_GPIOx, EEPROM_GPIO_Pin_CS)   // 使能
#define EEPROM_CS_HIGH()        GPIO_SetBits(EEPROM_GPIOx, EEPROM_GPIO_Pin_CS)     // 失能
#define EEPROM_ADDR_SIZE_MAX    0x8000                                             // EEPROM容量大小(32KB)
#define EEPROM_PAGE_SIZE        64                                                 // EEPROM页大小(字节)

// 函数声明
void EEPROM_GpioInit(void);
void EEPROM_SpiInit(void);
void EEPROM_DmaInit(void);
void EEPROM_NvicInit(void);

// 局部变量
uint8_t EEPROM_DmaTX_Buff[EEPROM_DMA_WR_BUFF_SIZE] = {0};   // DMA发送缓冲区
uint8_t EEPROM_DmaRX_Buff[EEPROM_DMA_RD_BUFF_SIZE] = {0};   // DMA接受缓冲区

/// @brief 外部扩展EEPROM初始化
/// @param  none
void EEPROM_Init(void){
    // 打开外设时钟
    RCC_EnableAPB2PeriphClk(EEPROM_GPIO_RCC_Periph | RCC_APB2_PERIPH_AFIO, ENABLE);
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_SPI2, ENABLE);
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA1, ENABLE);
    // SPI1引脚重映射
    GPIO_ConfigPinRemap(GPIO_RMP2_SPI2, ENABLE);
    // 初始化GPIO
    EEPROM_GpioInit();
    // 初始化SPI
    EEPROM_SpiInit();
    // 初始化DMA
    EEPROM_DmaInit();
    // 初始化NVIC
    EEPROM_NvicInit();
}

/// @brief Gpio引脚初始化
/// @param  none
void EEPROM_GpioInit(void){
    GPIO_InitType GPIO_InitStructure;
    // 初始化GPIO引脚
    GPIO_InitStructure.GPIO_Mode = EEPROM_GPIO_Mode_MOSI;
    GPIO_InitStructure.Pin = EEPROM_GPIO_Pin_MOSI;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitPeripheral(EEPROM_GPIOx, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Mode = EEPROM_GPIO_Mode_MISO;
    GPIO_InitStructure.Pin = EEPROM_GPIO_Pin_MISO;
    GPIO_InitPeripheral(EEPROM_GPIOx, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Mode = EEPROM_GPIO_Mode_SCK;
    GPIO_InitStructure.Pin = EEPROM_GPIO_Pin_SCK;
    GPIO_InitPeripheral(EEPROM_GPIOx, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Mode = EEPROM_GPIO_Mode_CS;
    GPIO_InitStructure.Pin = EEPROM_GPIO_Pin_CS;
    GPIO_InitPeripheral(EEPROM_GPIOx, &GPIO_InitStructure);

    // 引脚电平初始化
    EEPROM_CS_HIGH();
}

/// @brief SPI外设初始化
/// @param  none
void EEPROM_SpiInit(void){
    SPI_InitType SPI_InitStructure;
    // SPI配置
    SPI_InitStructure.DataDirection = EEPROM_Direction;
    SPI_InitStructure.SpiMode = EEPROM_Mode;
    SPI_InitStructure.DataLen = EEPROM_DataSize;
    SPI_InitStructure.CLKPOL = EEPROM_CPOL;
    SPI_InitStructure.CLKPHA = EEPROM_CPHA;
    SPI_InitStructure.NSS = EEPROM_NSS;
    SPI_InitStructure.BaudRatePres = EEPROM_BaudRatePres;
    SPI_InitStructure.FirstBit = EEPROM_FirstBit;
    SPI_InitStructure.CRCPoly = EEPROM_CRCPoly;
    SPI_Init(EEPROM_SPI, &SPI_InitStructure);
    // 打开SPI
    SPI_Enable(EEPROM_SPI, ENABLE);
}

/// @brief DMA外设初始化
/// @param  none
void EEPROM_DmaInit(void){
    DMA_InitType DMA_InitStructure;
    // dma发送初始化
    DMA_DeInit(EEPROM_DMA_Tx_Channel);
    DMA_InitStructure.PeriphAddr = EEPROM_DR_Address;
    DMA_InitStructure.MemAddr = (uint32_t)EEPROM_DmaTX_Buff;
    DMA_InitStructure.Direction = DMA_DIR_PERIPH_DST;
    DMA_InitStructure.BufSize = 0;
    DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
    DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE;
    DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE;
    DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.CircularMode = DMA_MODE_NORMAL;
    DMA_InitStructure.Priority = DMA_PRIORITY_VERY_HIGH;
    DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE;
    DMA_Init(EEPROM_DMA_Tx_Channel, &DMA_InitStructure);
    SPI_I2S_EnableDma(EEPROM_SPI, SPI_I2S_DMA_TX, ENABLE);
    DMA_EnableChannel(EEPROM_DMA_Tx_Channel, DISABLE);
    // dma接收初始化
    DMA_DeInit(EEPROM_DMA_Rx_Channel);
    DMA_InitStructure.PeriphAddr = EEPROM_DR_Address;
    DMA_InitStructure.MemAddr = (uint32_t)EEPROM_DmaRX_Buff;
    DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC;
    DMA_InitStructure.BufSize = sizeof(EEPROM_DmaRX_Buff);
    DMA_Init(EEPROM_DMA_Rx_Channel, &DMA_InitStructure);
    DMA_ConfigInt(EEPROM_DMA_Rx_Channel, DMA_INT_TXC, ENABLE);
    DMA_ConfigInt(EEPROM_DMA_Rx_Channel, DMA_INT_ERR, ENABLE);
    SPI_I2S_EnableDma(EEPROM_SPI, SPI_I2S_DMA_RX, ENABLE);
    DMA_EnableChannel(EEPROM_DMA_Rx_Channel, DISABLE);
}

/// @brief 嵌套中断初始化
/// @param  none
void EEPROM_NvicInit(void){
    NVIC_InitType NVIC_InitStructure;
    // 打开dma接收中断
    NVIC_InitStructure.NVIC_IRQChannel = EEPROM_DMA_Rx_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/// @brief 就绪状态检测
/// @param  none
/// @return 1就绪，0未就绪
uint8_t EEPROM_ReadyCheck(void){
    uint8_t os_err = 0;
    uint8_t eepromState = 0x01;
    
    EEPROM_CS_LOW();
    // 数据配置
    EEPROM_DmaTX_Buff[0] = EEPROMCMD_RDSR;
    EEPROM_DmaTX_Buff[1] = 0xff;
    // DMA配置
    EEPROM_DMA_Tx_Channel->TXNUM = 2;
    EEPROM_DMA_Rx_Channel->TXNUM = 2;
    // 开启DMA传输
    DMA_EnableChannel(EEPROM_DMA_Tx_Channel, ENABLE);
    DMA_EnableChannel(EEPROM_DMA_Rx_Channel, ENABLE);
    // 等待发送完成
    OSSemPend((OS_EVENT *)App_Sem_EEPROMRC,0,&os_err);
    EEPROM_CS_HIGH();
    // 获取读取数据
    eepromState = EEPROM_DmaRX_Buff[1];
    // 判断EEPROM是否就绪
    if((eepromState & 0x01) == 0)
        return 1;
    return 0;
}

/// @brief 写使能
/// @param  none
void EEPROM_WriteEnable(void){
    uint8_t os_err = 0;

    EEPROM_CS_LOW();
    // 数据配置
    EEPROM_DmaTX_Buff[0] = EEPROMCMD_WREN;
    // DMA配置
    EEPROM_DMA_Tx_Channel->TXNUM = 1;
    EEPROM_DMA_Rx_Channel->TXNUM = 1;
    // 开启DMA传输
    DMA_EnableChannel(EEPROM_DMA_Tx_Channel, ENABLE);
    DMA_EnableChannel(EEPROM_DMA_Rx_Channel, ENABLE);
    // 等待发送完成
    OSSemPend((OS_EVENT *)App_Sem_EEPROMRC,0,&os_err);
    EEPROM_CS_HIGH();
}

/// @brief DMA数据传输
/// @param addr 传输地址
/// @param writeData 需要写入的数据，读取时请写NULL
/// @param length 数据长度
/// @param EEPROM_CMD 传输命令
/// @return 0失败  1成功
uint8_t EEPROM_DmaTransfer(uint32_t addr, uint8_t * writeData, uint16_t length, uint8_t EEPROM_CMD){
    uint8_t os_err = 0;

    EEPROM_CS_LOW();
    EEPROM_DmaTX_Buff[0] = EEPROM_CMD;
    EEPROM_DmaTX_Buff[1] = (addr >> 8) & 0xff;
    EEPROM_DmaTX_Buff[2] = (addr) & 0xff;
    if(writeData == NULL)
        memset(&EEPROM_DmaTX_Buff[3], 0xff, length);
    else
        memcpy(&EEPROM_DmaTX_Buff[3], writeData, length);
    // DMA配置
    EEPROM_DMA_Tx_Channel->TXNUM = 3 + length;
    EEPROM_DMA_Rx_Channel->TXNUM = 3 + length;
    // 开启DMA传输
    DMA_EnableChannel(EEPROM_DMA_Tx_Channel, ENABLE);
    DMA_EnableChannel(EEPROM_DMA_Rx_Channel, ENABLE);
    // 等待发送完成(2秒超时)
    OSSemPend((OS_EVENT *)App_Sem_EEPROMRC,400,&os_err);
    if(os_err == OS_ERR_TIMEOUT){
        EEPROM_CS_HIGH();
        return DEF_FALSE;
    }
    EEPROM_CS_HIGH();
    return DEF_TRUE;
}

/// @brief 往EEPROM指定页面写入数据
/// @param addr 数据地址
/// @param writeData 数据内容
/// @param length 数据长度
/// @return 0失败  1成功
uint8_t EEPROM_WritePage(uint16_t addr, uint8_t * writeData, uint16_t length){
    uint16_t timeOutCount = 0;

    // EEPROM就绪检测
    while(EEPROM_ReadyCheck() == 0){
        timeOutCount++; 
        if(timeOutCount > 5000) 
            return DEF_FALSE;
    }
    // 写使能
    EEPROM_WriteEnable();
    // 往EEPROM写数据
    if(EEPROM_DmaTransfer(addr, writeData, length, EEPROMCMD_WRITE) == DEF_FALSE)
        return DEF_FALSE;
    return DEF_TRUE;
}

/// @brief 往EEPROM写入数据
/// @param addr 数据地址
/// @param writeData 数据内容
/// @param length 数据长度
/// @return 0失败  1成功
uint8_t EEPROM_WriteData(uint16_t addr, uint8_t * writeData, uint16_t length){
    uint8_t status = 0;
    uint8_t index = 0;
    uint16_t i = 0;
    uint16_t writeCount = 0;
    uint16_t writeLength = 0;
    uint16_t tempLength = length;

    // 地址范围判断
    if((addr + length) > EEPROM_ADDR_SIZE_MAX)
        return 0;
    // 写入数据在页面中位置
    index = addr % EEPROM_PAGE_SIZE;
    // 判断是否能一页写完
    if((index + length) <= EEPROM_PAGE_SIZE){
        return EEPROM_WritePage(addr, writeData, length);
    }
    // 需要分页写入
    if(index == 0){
        (tempLength % EEPROM_PAGE_SIZE == 0) ? (writeCount = tempLength / EEPROM_PAGE_SIZE) : (writeCount = tempLength / EEPROM_PAGE_SIZE + 1);
    }
    else{
        tempLength -= (EEPROM_PAGE_SIZE - index);
        (tempLength % EEPROM_PAGE_SIZE == 0) ? (writeCount = tempLength / EEPROM_PAGE_SIZE) : (writeCount = tempLength / EEPROM_PAGE_SIZE + 1);
        writeCount += 1;
    }
    for(i = 0; i < writeCount; i++){
        if(length >= EEPROM_PAGE_SIZE)
            writeLength = EEPROM_PAGE_SIZE - index;
        else
            writeLength = length;
        status = EEPROM_WritePage(addr, writeData, writeLength);
        if(status == 0)
            return 0;
        index = 0;
        addr += writeLength;
        length -= writeLength;
        writeData += writeLength;
    }
    return 1;
}

/// @brief 从EEPROM读取数据
/// @param addr 数据地址
/// @param readData 数据内容
/// @param length 数据长度
/// @return 0失败  1成功
uint8_t EEPROM_ReadData(uint16_t addr, uint8_t * readData, uint16_t length){
    uint16_t i = 0;
    uint16_t readLength = 0;
    uint16_t tempLength = length;
    uint16_t  timeOutCount = 0;

    // 判断地址范围
    if((addr + length) > EEPROM_ADDR_SIZE_MAX)
        return 0;
    // EEPROM就绪检测
    while(EEPROM_ReadyCheck() == 0){
        timeOutCount++;
        if(timeOutCount > 5000) return 0;
    }
    // 数据分段读取
    for(i = 0; i < length; i += readLength){
        /* 数据长度设定 */
        if(tempLength <= (EEPROM_DMA_RD_BUFF_SIZE - 3))
            readLength = tempLength;
        else
            readLength = EEPROM_DMA_RD_BUFF_SIZE - 3;
        /* 数据读取 */
        EEPROM_DmaTransfer(addr + i, NULL, readLength, EEPROMCMD_READ);
        // 将DMA接收缓冲的数据写入用户缓冲
        memcpy(readData + i, &EEPROM_DmaRX_Buff[3], readLength);
        tempLength -= readLength;
    }
    return 1;
}

/// @brief DMA接收中断处理函数
/// @param  none
void EEPROM_DMA_Rx_Function(void){
    OS_CPU_SR cpu_sr;
    OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR 		 */
    OSIntEnter();

    //关闭DMA
    DMA_EnableChannel(EEPROM_DMA_Rx_Channel, DISABLE);
    DMA_EnableChannel(EEPROM_DMA_Tx_Channel, DISABLE);
    //清除中断标志
    DMA_ClearFlag(EEPROM_DMA_Rx_FLAG, DMA1);
    //释放信号量
    OSSemPost((OS_EVENT *)App_Sem_EEPROMRC);

    OSIntExit(); /* Tell uC/OS-II that we are leaving the ISR 		 */
    OS_EXIT_CRITICAL();
}
